/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.aplikator.client.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.aplikator.client.Aplikator;
import org.aplikator.client.command.BindableCommand;
import org.aplikator.client.descriptor.ActionDTO;
import org.aplikator.client.descriptor.ApplicationDTO;
import org.aplikator.client.descriptor.MenuDTO;
import org.aplikator.client.panels.ContentsPanel;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.prefetch.RunAsyncCode;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionModel;
import com.google.gwt.view.client.TreeViewModel;

/**
 * The {@link TreeViewModel} used by the main menu.
 */
public class MainMenuTreeViewModel implements TreeViewModel {

    /**
     * The cell used to render categories.
     */
    private static class MenuCell extends AbstractCell<Category> {
        @Override
        public void render(Context context, Category value, SafeHtmlBuilder sb) {
            if (value != null) {
                sb.appendEscaped(value.getName());
            }
        }
    }

    /**
     * The cell used to render examples.
     */
    private static class ActionCell extends AbstractCell<ActionDTO> {
        @Override
        public void render(Context context, ActionDTO value, SafeHtmlBuilder sb) {
            if (value != null && value.getLocalizedName()!=null) {
                sb.appendEscaped(value.getLocalizedName());
            }
        }
    }

    public class Category {

        private final ListDataProvider<ActionDTO> examples = new ListDataProvider<ActionDTO>();
        private final String name;
        private NodeInfo<ActionDTO> nodeInfo;
        private final List<RunAsyncCode> splitPoints = new ArrayList<RunAsyncCode>();

        public Category(String name) {
            this.name = name;
        }

        public void addExample(ActionDTO example, RunAsyncCode splitPoint) {
            examples.getList().add(example);
            if (splitPoint != null) {
                splitPoints.add(splitPoint);
            }
            contentCategory.put(example, this);
            contentToken.put(Aplikator.getContentWidgetToken(example), example);
        }

        public String getName() {
            return name;
        }

        /**
         * Get the node info for the examples under this category.
         *
         * @return the node info
         */
        public NodeInfo<ActionDTO> getNodeInfo() {
            if (nodeInfo == null) {
                nodeInfo = new DefaultNodeInfo<ActionDTO>(examples, contentWidgetCell, selectionModel, null);
            }
            return nodeInfo;
        }

        /**
         * Get the list of split points to prefetch for this category.
         *
         * @return the list of classes in this category
         */
        public Iterable<RunAsyncCode> getSplitPoints() {
            return splitPoints;
        }
    }

    /**
     * The top level categories.
     */
    private final ListDataProvider<Category> categories = new ListDataProvider<Category>();

    /**
     * A mapping of {@link ContentWidget}s to their associated categories.
     */
    private final Map<ActionDTO, Category> contentCategory = new HashMap<ActionDTO, Category>();

    /**
     * The cell used to render examples.
     */
    private final ActionCell contentWidgetCell = new ActionCell();

    /**
     * A mapping of history tokens to their associated {@link ContentWidget}.
     */
    private final Map<String, ActionDTO> contentToken = new HashMap<String, ActionDTO>();

    /**
     * The selection model used to select examples.
     */
    private final SelectionModel<ActionDTO> selectionModel;

    public MainMenuTreeViewModel(ApplicationDTO application, ContentsPanel contents, SelectionModel<ActionDTO> selectionModel) {
        this.selectionModel = selectionModel;
        initializeTree(application, contents);
    }

    /**
     * Get the {@link Category} associated with a widget.
     *
     * @param widget
     *            the {@link ContentWidget}
     * @return the associated {@link Category}
     */
    public Category getCategoryForContentWidget(ActionDTO widget) {
        return contentCategory.get(widget);
    }

    /**
     * Get the content widget associated with the specified history token.
     *
     * @param token
     *            the history token
     * @return the associated {@link ContentWidget}
     */
    public ActionDTO getContentWidgetForToken(String token) {
        return contentToken.get(token);
    }

    public <T> NodeInfo<?> getNodeInfo(T value) {
        if (value == null) {
            // Return the top level categories.
            return new DefaultNodeInfo<Category>(categories, new MenuCell());
        } else if (value instanceof Category) {
            // Return the examples within the category.
            Category category = (Category) value;
            return category.getNodeInfo();
        }
        return null;
    }

    public boolean isLeaf(Object value) {
        return value != null && !(value instanceof Category);
    }

    /**
     * Get the set of all {@link ContentWidget}s used in the model.
     *
     * @return the {@link ContentWidget}s
     */
    Set<ActionDTO> getAllContentWidgets() {
        Set<ActionDTO> actions = new HashSet<ActionDTO>();
        for (Category category : categories.getList()) {
            for (ActionDTO example : category.examples.getList()) {
                actions.add(example);
            }
        }
        return actions;
    }

    /**
     * Initialize the top level categories in the tree.
     */
    private void initializeTree(ApplicationDTO application, final ContentsPanel contents) {
        List<Category> catList = categories.getList();

        for (MenuDTO menu : application.getMenus()) {
            Category category = new Category(menu.getLocalizedName());
            catList.add(category);

            for (final ActionDTO action : menu.getActions()) {
                final BindableCommand command = action.getCommand();
                command.bindMainPanel(contents);
                @SuppressWarnings("unused")
                Button b = new Button(action.getLocalizedName(), new ClickHandler() {

                    public void onClick(ClickEvent event) {
                        contents.setTitle(action.getLocalizedName());
                        command.execute();

                    }
                });
                category.addExample(action, null);
            }
        }
    }
}
